package nautilus.lab.activity;

/*
Note: these not exist or not work before Android 2.3

GLES20.glVertexAttribPointer
GLES20.glDrawElements
 */

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.os.Bundle;
import android.util.Log;


public class TwoDRenderer extends Activity {

	GLSurfaceView view;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        view = new GLSurfaceView(this);
        view.setEGLConfigChooser(8 , 8, 8, 8, 16, 0);
        view.setEGLContextClientVersion(2);
        //view.setRenderer(new TextTextureRenderer());
        view.setRenderer(new SimpleTexture2DRenderer(this));

        setContentView(view);
    }
    
    @Override
    protected void onResume() {
    	super.onResume();
    	view.onResume();
    }
    
    @Override
    protected void onPause() {
    	view.onPause();
    	super.onPause();
    }

}

class TextTextureRenderer implements GLSurfaceView.Renderer {
	/** Additional constants. */
	private static final int POSITION_HANDLE = 0;
	private static final int COLOR_HANDLE = 1;
	private static final int TEXTURE_HANDLE = 2;
	
	private static final int POSITION_DATA_SIZE_IN_ELEMENTS = 3;
	private static final int COLOR_DATA_SIZE_IN_ELEMENTS = 4;
	public static final int BYTES_PER_FLOAT = 4;
	public static final int BYTES_PER_SHORT = 2;

	private static final int STRIDE = (POSITION_DATA_SIZE_IN_ELEMENTS + COLOR_DATA_SIZE_IN_ELEMENTS) * BYTES_PER_FLOAT;
	
	private static final String kVertexShader =
	        "precision mediump float;                       \n" +
	        "uniform mat4 uMVP;                          	\n" +
	        "attribute vec3 aPosition;                   	\n" +
	        "attribute vec4 aColor;                     	\n" +
	        "attribute vec2 aTexCoord;						\n" +
	        "varying vec2 vTexCoord;						\n" + 
	        "void main() {                          \n" +
	        //"  gl_Position = uMVP * vec4(aPosition, 1.0); 	\n" +
	        "  gl_Position = vec4(aPosition, 1.0); 	\n" +
	        "  vTexCoord = aTexCoord;						\n" +
	        "}";

	private static final String kFragmentShader =
	        "precision mediump float;                   \n" +
	        "uniform sampler2D uTexture; 				\n" +
	        "varying vec2 vTexCoord;                     	\n" +
	        "void main() {                              \n" +
	        "    gl_FragColor = texture2D(uTexture, vTexCoord);                 \n" +
	        "}";
	
	private final float[] mPerspectiveMatrix = new float[16];
	private final float[] mOrthoMatrix = new float[16];
	private final float[] mViewMatrix = new float[16];
	private final float[] mMVP = new float[16];
	
	private float[] backColor = new float[]{0.1f, 0.1f, 0.1f, 1f};
	
	//Test: draw a rectangle with orthogonal matrix
	private float[] rect = new float[]{
			-0.5f, -0.5f, 0, 	0.0f, 1.0f,
			-0.5f,  0.5f, 0, 	0.0f, 0.0f, 
			0.5f,   0.5f, 0, 	1.0f, 0.0f, 
			0.5f,  -0.5f, 0, 	1.0f, 1.0f, 
		};
	private short[] indices = new short[]{0, 1, 2, 0, 2, 3};
	
	
	BufferObject testObject;
	//End test data
	
	//Camera data
	float eyeX = 0, eyeY = 0, eyeZ = -3f;
	float lookX = 0, lookY = 0, lookZ = 0;
	float upX = 0.0f, upY = 1.0f, upZ = 0.0f;
	
	//OpenGL handle
	private int mProgram;
	private int uMVPHandle;
	private int uSample2D;
	
	/**
	 * PixelXorXfermode is deprecated as of Android API 16 (Jelly Bean),
	 * and appears to be a no-op, so use this function instead
	*/
	public void invertColor(Canvas c, Bitmap b, int x, int y, int width, int height, int color) {
		int pixels[] = new int[width*height];
		int i;
		b.getPixels(pixels, 0, width, x, y, width, height);
		for (i = 0; i < width * height; i++) {
			pixels[i] ^= color;
		}
		c.drawBitmap(pixels, 0, width, x, y, width, height, /*hasAlpha=*/false, /*paint=*/null);
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		GLES20.glClearColor(backColor[0], backColor[1], backColor[2], backColor[3]);
		//Background color
		//GLES20.glEnable(GLES20.GL_TEXTURE_2D);
		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
		//GLES20.glEnable(GLES20.GL_CULL_FACE);
        //GLES20.glEnable(GLES20.GL_DEPTH_TEST);
		
		//prepare shader and GL program
		int vertextSharder = GraphNoColorInVertexRenderer.loadShader(GLES20.GL_VERTEX_SHADER, kVertexShader);
		int fragmentSharder = GraphNoColorInVertexRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, kFragmentShader);
		
		if (vertextSharder == 0 || fragmentSharder == 0) {
			Log.e("GraphNoColorInVertexRenderer", "Load shaders failed!");
			return;
		}
		
		mProgram = GLES20.glCreateProgram();
		GLES20.glAttachShader(mProgram, vertextSharder);
		GLES20.glAttachShader(mProgram, fragmentSharder);
		
		//IMPORTANT: set position for attribute
		GLES20.glBindAttribLocation(mProgram, POSITION_HANDLE,"aPosition");
		GLES20.glBindAttribLocation(mProgram, COLOR_HANDLE, "aColor");
		GLES20.glBindAttribLocation(mProgram, TEXTURE_HANDLE, "aTexCoord");
		
		GLES20.glLinkProgram(mProgram);
		
		int[] linked = { 0 };
        GLES20.glGetProgramiv(mProgram, GLES20.GL_LINK_STATUS, linked, 0);
        if (linked[0] == 0) {
            Log.e("GraphNoColorInVertexRenderer", GLES20.glGetProgramInfoLog(mProgram));
            return;
        }

        //Set camera position
        Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
        //Matrix.setIdentityM(mRotationM, 0);
        //Matrix.setIdentityM(mTranslationM, 0);
        //Matrix.setIdentityM(mModel, 0);
        
        testObject = new BufferObject(4, rect, indices, GLES20.GL_TRIANGLES);
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		float near = 0.1f;
		float far = 9.0f;
		float range = near - far;
		float aspect = (float)width/height;
		float fovy = (float)Math.tan(0.5 * (Math.PI - Math.toRadians(40)));
		
		GLES20.glViewport(0, 0, width, height);
		
		mPerspectiveMatrix[0] = fovy / aspect;
		mPerspectiveMatrix[1] = 0;
		mPerspectiveMatrix[2] = 0;
		mPerspectiveMatrix[3] = 0;

		mPerspectiveMatrix[4] = 0;
		mPerspectiveMatrix[5] = fovy;
		mPerspectiveMatrix[6] = 0;
        mPerspectiveMatrix[7] = 0;

        mPerspectiveMatrix[8] = 0;
        mPerspectiveMatrix[9] = 0; 
        mPerspectiveMatrix[10] = far / range;
        mPerspectiveMatrix[11] = -1;

        mPerspectiveMatrix[12] = 0;
        mPerspectiveMatrix[13] = 0;
        mPerspectiveMatrix[14] = near * far / range;
        mPerspectiveMatrix[15] = 0;
        
        Matrix.orthoM(mOrthoMatrix, 0, -aspect, aspect, -1, 1, -1, far);
        //Matrix.orthoM(mOrthoMatrix, 0, 0, width, height, 0, -1, far);
        
        //Please check
//        for(int i=0; i<16; i++)
//        	mOrthoMatrix[i] = 0;
//        mOrthoMatrix[0] = 2 / width;
//        mOrthoMatrix[5] = -2 / height;
//        mOrthoMatrix[10] = -2/( far - near );
//        mOrthoMatrix[12] = -1;
//        mOrthoMatrix[13] = 1;
//        mOrthoMatrix[14] = - (far + near) / (far - near);
//        mOrthoMatrix[15] = 1;
	}
	
	@Override
	public void onDrawFrame(GL10 gl) {
		GLES20.glClearColor(backColor[0], backColor[1], backColor[2], backColor[3]);
		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
		
		GLES20.glUseProgram(mProgram);
		uMVPHandle = GLES20.glGetUniformLocation(mProgram, "uMVP");
		//uSample2D = GLES20.glGetUniformLocation(mProgram, "uTexture");
		
		
//		Matrix.multiplyMM(mModelView, 0, mViewMatrix, 0, mModel, 0);
//		Matrix.multiplyMM(mMVP, 0, mOrthoMatrix, 0, mModelView, 0);
		Matrix.multiplyMM(mMVP, 0, mOrthoMatrix, 0, mViewMatrix, 0);
		
		GLES20.glUniformMatrix4fv(uMVPHandle, 1, false, mMVP, 0);
		//GLES20.glUniform1i(uSample2D, 0);
		
		testObject.render();
	}
	
	/**
	 * Create and compile a shader with specified type
	 * @param type type of shader (GLES20.GL_VERTEX_SHADER or GLES20.GL_FRAGMENT_SHADER)
	 * @param shaderCode String contains shader code
	 * @return id for the shader
	 */
	public static int loadShader(int type, String shaderCode){
		int shader = GLES20.glCreateShader(type);
		int[] compiled = { 0 };
		if(shader == 0) return 0;
			
		GLES20.glShaderSource(shader, shaderCode);
		GLES20.glCompileShader(shader);
		
		GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {
            Log.e("GraphNoColorInVertexRenderer", GLES20.glGetShaderInfoLog(shader));
        }
        return shader;
	}
	
	/**
	 * 
	 * @param target one of value GLES20.GL_ARRAY_BUFFER or GLES20.GL_ELEMENT_ARRAY_BUFFER
	 */
	static void initVertexBuffer(int target, float[] data, int bufferId){
		int size = data.length * BYTES_PER_FLOAT;
		FloatBuffer fb = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asFloatBuffer();
		fb.put(data);
		fb.position(0);
		
		GLES20.glBindBuffer(target, bufferId);
		GLES20.glBufferData(target, size, fb, GLES20.GL_STATIC_DRAW);
		GLES20.glBindBuffer(target, 0);
	}
	
	static void initIndexBuffer(int target, short[] data, int bufferId){
		int size = data.length * BYTES_PER_SHORT;
		ShortBuffer fb = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asShortBuffer();
		fb.put(data);
		fb.position(0);
		
		GLES20.glBindBuffer(target, bufferId);
		GLES20.glBufferData(target, size, fb, GLES20.GL_STATIC_DRAW);
		GLES20.glBindBuffer(target, 0);
	}
	
	
	class BufferObject {
		boolean mUseNormals;
		boolean mUseTexCoords;
		int mVertexCount;
		int mDrawingPrimitive;
		int mNumIndices;
		FloatBuffer uvBuffer;
		int[] textureId;
		
		/**
		 * {  }
		 */
		final int[] buffers = {0, 0};
		
		BufferObject(int vertexCount, float[] vertices, short[] indices, int drawingPrimity) {
			//These commands must be invoked in dispatch-thread
			GLES20.glGenBuffers(2, buffers, 0);
			mVertexCount = vertexCount;
		    initVertexBuffer(GLES20.GL_ARRAY_BUFFER, vertices, buffers[0]);
		    initIndexBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indices, buffers[1]);
		    mDrawingPrimitive = drawingPrimity;
		    mNumIndices = indices.length;
		    
		    createTextTexture("Test message!");
		}
		
		void createTextTexture(String msg) {
//			float[] uvs = new float[]{
//				0f, 0f,
//				0f, 1f,
//				1f, 1f,
//				1f, 0f,
//			};
			float[] uvs = new float[]{
					-0.5f, -0.5f,
					-0.5f,  0.5f, 
					0.5f,   0.5f, 
					0.5f,  -0.5f 
				};
			
			Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
			paint.setColor(Color.RED);
			Bitmap bmp = Bitmap.createBitmap(100, 50, Bitmap.Config.ARGB_8888);
			Canvas canvas = new Canvas(bmp);
			canvas.drawColor(Color.WHITE);
			
			canvas.drawText(msg, 0, 25, paint);
			
			//Generate buffer
			ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * BYTES_PER_FLOAT);
			uvBuffer = bb.order(ByteOrder.nativeOrder()).asFloatBuffer().put(uvs);
			uvBuffer.position(0);
			
			textureId = new int[1];
			GLES20.glGenTextures(1, textureId, 0);
			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0]);
			
			GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
			
			// Set filtering
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
			 
			// Set wrapping mode
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
			bmp.recycle();
		}
		
		void render() {
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[0]);
			
			GLES20.glEnableVertexAttribArray(POSITION_HANDLE);
			GLES20.glVertexAttribPointer(POSITION_HANDLE, 3, GLES20.GL_FLOAT, false, STRIDE, 0);                
			
			//bind Color buffer
			//GLES20.glEnableVertexAttribArray(COLOR_HANDLE);
			//GLES20.glVertexAttribPointer(COLOR_HANDLE, 4, GLES20.GL_FLOAT, false, STRIDE, 12);
			
			//bind texture
			//GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, textureId[0]);
			GLES20.glEnableVertexAttribArray(TEXTURE_HANDLE);
			GLES20.glVertexAttribPointer(TEXTURE_HANDLE, 2, GLES20.GL_FLOAT, false, 0, uvBuffer);
			
			
			uSample2D = GLES20.glGetUniformLocation(mProgram, "uTexture");
			
			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0]);
			GLES20.glUniform1i(uSample2D, 0);
			
			//Now, Bind index buffer
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
			GLES20.glDrawElements(mDrawingPrimitive, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0);
			
		    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
		    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
		
		    GLES20.glDisableVertexAttribArray(POSITION_HANDLE);
		    GLES20.glDisableVertexAttribArray(COLOR_HANDLE);
		    GLES20.glDisableVertexAttribArray(TEXTURE_HANDLE);
		}
		
		public void release(){
			
			if(buffers[0] <=0 || buffers[1] <= 0)
				return;
			GLES20.glDeleteBuffers(2, buffers, 0);
		    buffers[0] = 0;
		    buffers[1] = 0;
		}
	}
}

class GraphNoColorInVertexRenderer implements GLSurfaceView.Renderer {
	/** Additional constants. */
	private static final int POSITION_HANDLE = 0;
	private static final int COLOR_HANDLE = 1;
	
	private static final int POSITION_DATA_SIZE_IN_ELEMENTS = 3;
	private static final int COLOR_DATA_SIZE_IN_ELEMENTS = 4;
	public static final int BYTES_PER_FLOAT = 4;
	public static final int BYTES_PER_SHORT = 2;

	private static final int STRIDE = (POSITION_DATA_SIZE_IN_ELEMENTS + COLOR_DATA_SIZE_IN_ELEMENTS) * BYTES_PER_FLOAT;
	
	private static final String kVertexShader =
	        "precision mediump float;                       \n" +
	        "uniform mat4 uMVP;                          	\n" +
	        "attribute vec3 aPosition;                   	\n" +
	        "attribute vec4 aColor;                     	\n" +
	        "//attribute vec2 aTexCoord;						\n" +
	        "varying vec4 vColor;                     		\n" +
	        "//varying vec2 vTexCoord;						\n" + 
	        "void main() {                               	\n" +
	        "  vColor = aColor; 							\n" +
	        "  gl_Position = uMVP * vec4(aPosition, 1.0); 	\n" +
	        "  //vTexCoord = aTexCoord;						\n" +
	        "}";

	private static final String kFragmentShader =
	        "precision mediump float;                   \n" +
	        "//uniform sample2D uTexture; 				\n" +
	        "varying vec4 vColor;                     	\n" +
	        "void main() {                              \n" +
	        "    gl_FragColor = vColor;                 \n" +
	        "}";
	
	private final float[] mPerspectiveMatrix = new float[16];
	private final float[] mOrthoMatrix = new float[16];
	private final float[] mModelView = new float[16];
	private final float[] mViewMatrix = new float[16];
	private final float[] mMVP = new float[16];
	private final float[] mModel = new float[16];
	private final float[] mRotationM = new float[16];
	private final float[] mTranslationM = new float[16];
	
	private float[] backColor = new float[]{0.1f, 0.1f, 0.1f, 1f};
	
	//Test: draw a rectangle with orthogonal matrix
	private float[] rect = new float[]{
			-0.5f, -0.5f, 0, 	0, 1, 0, 1,
			-0.5f,  0.5f, 0, 	0, 1, 0, 1, 
			0.5f,   0.5f, 0, 	0, 1, 0, 1, 
			0.5f,  -0.5f, 0, 	0, 1, 0, 1 
		};
	private short[] indices = new short[]{0, 1, 2, 0, 2, 3};
	
	
	BufferObject testObject;
	//End test data
	
	//Camera data
	float eyeX = 0, eyeY = 0, eyeZ = -3f;
	float lookX = 0, lookY = 0, lookZ = 0;
	float upX = 0.0f, upY = 1.0f, upZ = 0.0f;
	
	//OpenGL handle
	private int mProgram;
	private int uMVPHandle;
	
	/**
	 * PixelXorXfermode is deprecated as of Android API 16 (Jelly Bean),
	 * and appears to be a no-op, so use this function instead
	*/
	public void invertColor(Canvas c, Bitmap b, int x, int y, int width, int height, int color) {
		int pixels[] = new int[width*height];
		int i;
		b.getPixels(pixels, 0, width, x, y, width, height);
		for (i = 0; i < width * height; i++) {
			pixels[i] ^= color;
		}
		c.drawBitmap(pixels, 0, width, x, y, width, height, /*hasAlpha=*/false, /*paint=*/null);
	}
	
	FloatBuffer uvBuffer;
	int[] textureId;
	void createTextTexture(String msg) {
		float[] uvs = new float[]{
			-0.4f, 0.4f,
			0.4f, 0.4f,
			0.4f, -0.4f,
			-0.4f, -0.4f,
		};
		Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
		paint.setColor(Color.RED);
		Bitmap bmp = Bitmap.createBitmap(100, 50, Bitmap.Config.ARGB_8888);
		Canvas canvas = new Canvas(bmp);
		canvas.drawColor(Color.WHITE);
		
		canvas.drawText(msg, 0, 25, paint);
		
		//Generate buffer
		ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * BYTES_PER_FLOAT);
		uvBuffer = bb.order(ByteOrder.nativeOrder()).asFloatBuffer().put(uvs);
		uvBuffer.position(0);
		
		textureId = new int[1];
		GLES20.glGenTextures(1, textureId, 0);
		GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0]);
		// Set filtering
		  GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
		                GLES20.GL_LINEAR);
		  GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
		                GLES20.GL_LINEAR);
		 
		  // Set wrapping mode
		  GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
		          GLES20.GL_CLAMP_TO_EDGE);
		  GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
				  GLES20.GL_CLAMP_TO_EDGE);
		GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
		bmp.recycle();
	}
	
	void renderTexture() {
//		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, textureId[0]);
//		
//		GLES20.glEnableVertexAttribArray(POSITION_HANDLE);
//		GLES20.glVertexAttribPointer(POSITION_HANDLE, 3, GLES20.GL_FLOAT, false, STRIDE, 0);                
//		
//		GLES20.glEnableVertexAttribArray(COLOR_HANDLE);
//		GLES20.glVertexAttribPointer(COLOR_HANDLE, 4, GLES20.GL_FLOAT, false, STRIDE, 12);
//		
//		GLES20.glDrawArrays(mDrawingPrimitive, 0, mVertexCount);
//		
//	    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
//	
//	    GLES20.glDisableVertexAttribArray(POSITION_HANDLE);
//	    GLES20.glDisableVertexAttribArray(COLOR_HANDLE);
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		GLES20.glClearColor(backColor[0], backColor[1], backColor[2], backColor[3]);
		//Background color
		//GLES20.glEnable(GLES20.GL_TEXTURE_2D);
		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
		//GLES20.glEnable(GLES20.GL_CULL_FACE);
        //GLES20.glEnable(GLES20.GL_DEPTH_TEST);
		
		//prepare shader and GL program
		int vertextSharder = GraphNoColorInVertexRenderer.loadShader(GLES20.GL_VERTEX_SHADER, kVertexShader);
		int fragmentSharder = GraphNoColorInVertexRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, kFragmentShader);
		
		if (vertextSharder == 0 || fragmentSharder == 0) {
			Log.e("GraphNoColorInVertexRenderer", "Load shaders failed!");
			return;
		}
		
		mProgram = GLES20.glCreateProgram();
		GLES20.glAttachShader(mProgram, vertextSharder);
		GLES20.glAttachShader(mProgram, fragmentSharder);
		
		//IMPORTANT: set position for attribute
		GLES20.glBindAttribLocation(mProgram, POSITION_HANDLE,"aPosition");
		GLES20.glBindAttribLocation(mProgram, COLOR_HANDLE, "aColor");
		
		GLES20.glLinkProgram(mProgram);
		
		int[] linked = { 0 };
        GLES20.glGetProgramiv(mProgram, GLES20.GL_LINK_STATUS, linked, 0);
        if (linked[0] == 0) {
            Log.e("GraphNoColorInVertexRenderer", GLES20.glGetProgramInfoLog(mProgram));
            return;
        }

        //Set camera position
        Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
        //Matrix.setIdentityM(mRotationM, 0);
        //Matrix.setIdentityM(mTranslationM, 0);
        //Matrix.setIdentityM(mModel, 0);
        
        testObject = new BufferObject(4, rect, indices, GLES20.GL_TRIANGLES);
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		float near = 0.1f;
		float far = 9.0f;
		float range = near - far;
		float aspect = (float)width/height;
		float fovy = (float)Math.tan(0.5 * (Math.PI - Math.toRadians(40)));
		
		GLES20.glViewport(0, 0, width, height);
		
		mPerspectiveMatrix[0] = fovy / aspect;
		mPerspectiveMatrix[1] = 0;
		mPerspectiveMatrix[2] = 0;
		mPerspectiveMatrix[3] = 0;

		mPerspectiveMatrix[4] = 0;
		mPerspectiveMatrix[5] = fovy;
		mPerspectiveMatrix[6] = 0;
        mPerspectiveMatrix[7] = 0;

        mPerspectiveMatrix[8] = 0;
        mPerspectiveMatrix[9] = 0; 
        mPerspectiveMatrix[10] = far / range;
        mPerspectiveMatrix[11] = -1;

        mPerspectiveMatrix[12] = 0;
        mPerspectiveMatrix[13] = 0;
        mPerspectiveMatrix[14] = near * far / range;
        mPerspectiveMatrix[15] = 0;
        
        Matrix.orthoM(mOrthoMatrix, 0, -aspect, aspect, -1, 1, -1, far);
        //Matrix.orthoM(mOrthoMatrix, 0, 0, width, height, 0, -1, far);
        
        //Please check
//        for(int i=0; i<16; i++)
//        	mOrthoMatrix[i] = 0;
//        mOrthoMatrix[0] = 2 / width;
//        mOrthoMatrix[5] = -2 / height;
//        mOrthoMatrix[10] = -2/( far - near );
//        mOrthoMatrix[12] = -1;
//        mOrthoMatrix[13] = 1;
//        mOrthoMatrix[14] = - (far + near) / (far - near);
//        mOrthoMatrix[15] = 1;
	}
	
	@Override
	public void onDrawFrame(GL10 gl) {
		GLES20.glClearColor(backColor[0], backColor[1], backColor[2], backColor[3]);
		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
		
		GLES20.glUseProgram(mProgram);
		uMVPHandle = GLES20.glGetUniformLocation(mProgram, "uMVP");
		
//		Matrix.multiplyMM(mModelView, 0, mViewMatrix, 0, mModel, 0);
//		Matrix.multiplyMM(mMVP, 0, mOrthoMatrix, 0, mModelView, 0);
		Matrix.multiplyMM(mMVP, 0, mOrthoMatrix, 0, mViewMatrix, 0);
		//Setting the camera
		GLES20.glUniformMatrix4fv(uMVPHandle, 1, false, mMVP, 0);
		
		testObject.render();
	}
	
	/**
	 * Create and compile a shader with specified type
	 * @param type type of shader (GLES20.GL_VERTEX_SHADER or GLES20.GL_FRAGMENT_SHADER)
	 * @param shaderCode String contains shader code
	 * @return id for the shader
	 */
	public static int loadShader(int type, String shaderCode){
		int shader = GLES20.glCreateShader(type);
		int[] compiled = { 0 };
		if(shader == 0) return 0;
			
		GLES20.glShaderSource(shader, shaderCode);
		GLES20.glCompileShader(shader);
		
		GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {
            Log.e("GraphNoColorInVertexRenderer", GLES20.glGetShaderInfoLog(shader));
        }
        return shader;
	}
	
	/**
	 * 
	 * @param target one of value GLES20.GL_ARRAY_BUFFER or GLES20.GL_ELEMENT_ARRAY_BUFFER
	 */
	static void initVertexBuffer(int target, float[] data, int bufferId){
		int size = data.length * BYTES_PER_FLOAT;
		FloatBuffer fb = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asFloatBuffer();
		fb.put(data);
		fb.position(0);
		
		GLES20.glBindBuffer(target, bufferId);
		GLES20.glBufferData(target, size, fb, GLES20.GL_STATIC_DRAW);
		GLES20.glBindBuffer(target, 0);
	}
	
	static void initIndexBuffer(int target, short[] data, int bufferId){
		int size = data.length * BYTES_PER_SHORT;
		ShortBuffer fb = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asShortBuffer();
		fb.put(data);
		fb.position(0);
		
		GLES20.glBindBuffer(target, bufferId);
		GLES20.glBufferData(target, size, fb, GLES20.GL_STATIC_DRAW);
		GLES20.glBindBuffer(target, 0);
	}
	
	
	class BufferObject {
		boolean mUseNormals;
		boolean mUseTexCoords;
		int mVertexCount;
		int mDrawingPrimitive;
		int mNumIndices;
		
		/**
		 * {  }
		 */
		final int[] buffers = {0, 0};
		
		BufferObject(int vertexCount, float[] vertices, short[] indices, int drawingPrimity) {
			//These commands must be invoked in dispatch-thread
			GLES20.glGenBuffers(2, buffers, 0);
			mVertexCount = vertexCount;
		    initVertexBuffer(GLES20.GL_ARRAY_BUFFER, vertices, buffers[0]);
		    initIndexBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indices, buffers[1]);
		    mDrawingPrimitive = drawingPrimity;
		    mNumIndices = indices.length;
		}
		
		void render() {
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[0]);
			
			GLES20.glEnableVertexAttribArray(POSITION_HANDLE);
			GLES20.glVertexAttribPointer(POSITION_HANDLE, 3, GLES20.GL_FLOAT, false, STRIDE, 0);                
			
			//bind Color buffer
			GLES20.glEnableVertexAttribArray(COLOR_HANDLE);
			GLES20.glVertexAttribPointer(COLOR_HANDLE, 4, GLES20.GL_FLOAT, false, STRIDE, 12);
			
			
			//Now, Bind index buffer
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
			GLES20.glDrawElements(mDrawingPrimitive, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0);
			
		    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
		    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
		
		    GLES20.glDisableVertexAttribArray(POSITION_HANDLE);
		    GLES20.glDisableVertexAttribArray(COLOR_HANDLE);
		}
		
		public void release(){
			
			if(buffers[0] <=0 || buffers[1] <= 0)
				return;
			GLES20.glDeleteBuffers(2, buffers, 0);
		    buffers[0] = 0;
		    buffers[1] = 0;
		}
	}
}